package com.example.certificate.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisTemplateUtil {

    private Logger logger = LoggerFactory.getLogger(RedisTemplateUtil.class);

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    public RedisTemplateUtil(RedisTemplate<String,Object> redisTemplate){
        this.redisTemplate = redisTemplate;
    }

    /**
     * 设置key的有效时间
     * @param key
     * @param time
     * @param timeUnit
     * @return
     */
    public boolean expire(String key, long time, TimeUnit timeUnit){
        Boolean expire = false;
        try{
            if(time>0){
                expire = redisTemplate.expire(key, time, timeUnit);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return expire;
    }

    public Boolean expire(String key, Date date){
        boolean expireStatus = false;
        try {
            if(StringUtils.isNotBlank(key)){
                expireStatus = redisTemplate.expireAt(key,date);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return expireStatus;
    }

    /**
     * 获取key的过期时间
     * 0为永久有效
     * 单位为秒
     * @param key
     * @return
     */
    public Long getExpires(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 获取key到期时间并且指定返回的时间单位 秒 小时 毫秒 分钟等
     * @param key
     * @param timeUnit
     * @return
     */
    public long getExpres(String key,TimeUnit timeUnit){
        return redisTemplate.getExpire(key,timeUnit);
    }

    /**
     * 判断key是否存在
     * @param key
     * @return
     */
    public Boolean hasKey(String key){
        return redisTemplate.hasKey(key);
    }

    /**
     * 获取key对应的val值
     * @param key
     * @return
     */
    public Object getVal(String key){
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除单个key或多个key
     * @param key
     */
    public void delete(String... key){
        if(key.length > 0 && key != null){
            if(key.length == 1){
                redisTemplate.delete(key[0]);
            }else {
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    /**
     * 把key转换为byte[]数组
     * @param key
     * @return
     */
    public byte[] dump(String key){
        return redisTemplate.dump(key);
    }

    /**
     * 查找匹配的key值，返回一个Set集合类型
     * @param patten
     * @return
     */
    public Set<String> getPatternKey(String patten) {
        return redisTemplate.keys(patten);
    }

    /**
     * 修改redis中key的名称
     * @param oldKey
     * @param newKey
     */
    public void renameKey(String oldKey,String newKey){
        redisTemplate.rename(oldKey,newKey);
    }

    /**
     * 返回redis中key值的类型
     * @param key
     * @return
     */
    public DataType getKeyType(String key){
        return redisTemplate.type(key);
    }

    /**
     * 如果旧值存在则替换为新值
     * @param oldKey
     * @param newKey
     * @return
     */
    public Boolean renameIfAbsent(String oldKey,String newKey){
        return redisTemplate.renameIfAbsent(oldKey,newKey);
    }

    /**
     * 从redis中随机取出一个key
     * @return
     */
    public String randomKey(){
        return redisTemplate.randomKey();
    }

    /**
     * 将key持久化保存
     * @param key
     * @return
     */
    public Boolean persistKey(String key){
        return redisTemplate.persist(key);
    }

    /**
     * 将key移到指定的数据库中
     * @param key
     * @param dataIndex
     * @return
     */
    public Boolean move(String key,Integer dataIndex){
        return redisTemplate.move(key,dataIndex);
    }

    /**
     * 设置key值
     * @param key
     * @param val
     */
    public void setKey(String key,Object val){
        redisTemplate.opsForValue().set(key,val);
    }

    /**
     * 设置key的val并设置过期时间
     * @param key
     * @param val
     * @param time
     * @param unit
     */
    public void setValExpire(String key,Object val,int time,TimeUnit unit){
        redisTemplate.opsForValue().set(key, val,time,unit);//向redis里存入数据和
    }

    public void setValExpire(String key,Object val){
        redisTemplate.opsForValue().set(key, val,60*5,TimeUnit.SECONDS);//向redis里存入数据和
    }

    /**
     * 返回key指定的长度部分
     * @param key
     * @param start
     * @param end
     * @return
     */
    public String getCharacterRange(String key,Integer start,Integer end){
        return redisTemplate.opsForValue().get(key,start,end);
    }

    /**
     * 获取多个key的值
     * @param keys
     * @return
     */
    public List<Object> multiGet(Collection<String> keys){
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 在key值原有的基础上进行追加
     * @param key
     * @param val
     */
    public void append(String key,String val){
        redisTemplate.opsForValue().append(key, val);
    }

    /**
     * 已增量的方式添加double
     * @param key
     * @param increment
     * @return
     */
    public Double incrDouble(String key,Double increment){
        return redisTemplate.opsForValue().increment(key,increment);
    }

    /**
     * 负数自减，正数自增
     * @param key
     * @param increment
     * @return
     */
    public Long incrByLong(String key,Integer increment){
        return redisTemplate.opsForValue().increment(key,increment);
    }

    /**
     * 获取key的length
     * @param key
     * @return
     */
    public long getKeyLen(String key){
        return redisTemplate.opsForValue().size(key);
    }

    /**
     * 设置map集合到redis中
     * 批量设置 k v 如果key存在则覆盖val
     * @param map
     */
    public void multiSet(Map map){
        redisTemplate.opsForValue().multiSet(map);
    }

    /**
     * 如果map集合中的Key存在则不做任何处理，（注意： 一个key都不重复的情况下才会执行此操作）
     * 如果不存在则添加
     * @param map
     * @return
     */
    public boolean multiSetIfAbsent(Map map){
        return redisTemplate.opsForValue().multiSetIfAbsent(map);
    }

    /**
     * 替换对应key的val值 并且返回旧的val
     * @param key
     * @param val
     * @return
     */
    public Object getAndSet(String key,Object val){ return redisTemplate.opsForValue().getAndSet(key,val);}
    /**
     * 新增地理位置
     * @param key
     * @param point
     * @param city
     * @return
     * Long addedNum = redisTemplate.opsForGeo().add("geo", new Point(113.27, 23.13), "广州");
     */
    public Long addGeo(String key, Point point, String city){
        return redisTemplate.opsForGeo().add(key,point,city);
    }

    public long addGeo(String key, Map map){
        return redisTemplate.opsForGeo().add(key,map);
    }

    /**
     * 返回多个城市的经纬度信息
     * @param key
     * @param city
     * @return
     */
    public List<Point> points(String key,String... city){
        return redisTemplate.opsForGeo().position(key,city);
    }

    /**
     * 返回两个城市之间的距离
     * @param key
     * @param city
     * @return
     */
    public Distance distanceKilometers(String key, String city, String city1, RedisGeoCommands.DistanceUnit unit){
        Distance distance = redisTemplate.opsForGeo()
                .distance(key,city,city1,unit);
        return distance;
    }

    /**
     * 获取两个位置之间的距离
     * @param point
     * @param point2
     * @return
     */
    public Double getDistance(Point point, Point point2,RedisGeoCommands.DistanceUnit unit){
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        //首先存入客户端上传的经纬度和指定地点的经纬度
        Map<String,Object> map = new HashMap<>();
        // 假如客户端的传的是北京西站的经纬度,指定地点是北京南站
        map.put("BJXZ",point);
        map.put("BJNZ",point2);
        // 将这些地址数据保存到redis中
        geoOperations.add("GET_DISTANCE",map);
        // 调用方法,计算北京西站与北京南站之间的距离;
        double val = geoOperations.distance("GET_DISTANCE", "BJXZ", "BJNZ",unit).getValue();
        return val;
    }

    /**
     * 根据计算经纬度方圆多少范围内的地理位置信息
     * @param key
     * @param lon
     * @param lat
     * @param scope
     * @param metrics
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> rangeOfInformation(String key, Double lon, Double lat, int scope, Metrics metrics){
        Circle circle = new Circle(new Point(lon, lat), new Distance(scope,metrics));//Point(经度, 纬度) Distance(距离量, 距离单位)
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending();
        GeoResults<RedisGeoCommands.GeoLocation<Object>> results = redisTemplate.opsForGeo()
                .radius(key,circle,args);//params: key, Circle, GeoRadiusCommandArgs
        return results;
    }

    /**
     * 已某个城市为中心查询方圆多少长度内的地理位置信息
     * @param key
     * @param city
     * @param scope
     * @param metrics
     * @param limit
     * @return
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> cityScope(String key,String city,int scope,Metrics metrics,int limit){
        Distance distance = new Distance(scope,metrics);//params: 距离量, 距离单位
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(limit);
        GeoResults<RedisGeoCommands.GeoLocation<Object>>  results = redisTemplate.opsForGeo() .radius(key,city,distance,args);//params: key, 地方名称, Circle, GeoRadiusCommandArgs
        return results;
    }

    /**
     * 移除地理位置信息
     * @param key
     * @param city
     * @return
     */
    public boolean removeGeo(String key, String city){
        return redisTemplate.opsForGeo().remove(key,city) > 0;
    }


    public Boolean removeGeo(String key,String... city){
        return redisTemplate.opsForGeo().remove(key,city) > 0;
    }
}
